#!/usr/bin/env bash
# === 查看磁盘状态（合并改进版）===
# 作者: ym（合并增强 by ChatGPT）
# 用法: bash <(curl -sSfLk https://3411.s.kuaicdn.cn:34112/shell/yingjian/Disk_state.sh)
# shellcheck disable=SC2002,SC2009,SC2086,SC2261

# -----------------------------
# 统一输出（保持原有风格）
# -----------------------------
output_info() {
    # disk_info 是 df -h 的整行
    disk_info1="$(echo "$disk_info" | awk '{printf "%-12s %-6s  %-4s   %-6s\n", $1, $2, $5, $4}')"
    disk_info6="$(echo "$disk_info" | awk '{print $6}')"
    printf "%-14s[%02d]  %s  $state%s\033[0m\n" "$item" "$did" "$disk_info1" "$disk_info6"
}

# -----------------------------
# 新增：判断/打印未挂载
# -----------------------------
is_mounted() {
    local dev="$1"
    df -P 2>/dev/null | awk 'NR>1 {print $1}' | grep -qx "$dev"
}

print_failed_line() {
    # $1=设备(或分区)，$2=显示用的 did
    local dev="$1"; local _did="${2:-99}"
    # 占位列：(设备名/Size/Use%/Avail) 用 '-'，挂载点显示 'Failed'
    # 与 output_info 对齐：设备名[did]  <12列> <6列> <4列> <6列>  挂载点
    printf "%-14s[%02d]  %-12s %-6s  %-4s   %-6s  %s\n" \
        "$dev" "$_did" "-" "-" "-" "-" "Failed"
}

# -----------------------------
# 系统盘小节（保持原有风格；根分区可写测试后清理）
# -----------------------------
show_system_disk() {
    for item in ${system_disk_list}; do
        state=
        if ! (echo 1 > /.test) 2>/dev/null; then
            state="\033[4m"  # 下划线：根不可写/疑似 I/O 问题
        fi
        rm -f /.test 2>/dev/null
        disk_info="$(df -h 2>/dev/null | grep -E '[[:space:]]/$')"
        did="$(did_get "$item")"
        output_info
    done
}

# -----------------------------
# 单个设备/分区状态判断（改进：未挂载=>Failed 专行输出）
# -----------------------------
disk_state_get() {
    item="$1"
    state=

    # 未挂载：直接打印 Failed，不再走 output_info 的 df 解析
    if ! is_mounted "$item"; then
        did="$(did_get "$item")"
        print_failed_line "$item" "$did"
        return
    fi

    # 判满：用可用块数阈值（保持原阈值<50KB）；列解析用 df -P 稳定
    if (("$(df -P "$item" 2>/dev/null | awk 'NR==2{print $4}')" < 50)); then
        state="\033[5m"  # 闪烁：空间极低
        disk_info="$(df -h 2>/dev/null | grep -E "^$item[[:space:]]" | sort -u)"
        output_info
        return
    fi

    # 判写：拿挂载点做一次真实写测试并清理
    mnt="$(df -P "$item" 2>/dev/null | awk 'NR==2{print $6}')"
    tmp="$mnt/.disk_state_test.$$"
    if ! ( : > "$tmp" ) 2>/dev/null; then
        state="\033[4m"  # 下划线：I/O 异常或不可写
        disk_info="$(df -h 2>/dev/null | grep -E "^$item[[:space:]]" | sort -u)"
        output_info
        return
    else
        rm -f "$tmp" 2>/dev/null
    fi

    # 正常
    disk_info="$(df -h 2>/dev/null | grep -E "^$item[[:space:]]" | sort -u)"
    output_info
}

# -----------------------------
# NVMe 数据盘
# -----------------------------
show_nvme_data_disk() {
    nvme_disk_list=$(echo "$data_disk_list" | grep nvme)
    if [ -z "$nvme_disk_list" ]; then return; fi
    did="99"
    for nvme in ${nvme_disk_list}; do
        if [[ "$(lsblk -p 2>/dev/null | grep -c "${nvme}p")" != "0" ]]; then
            echo "$nvme"
            nvme_list=$(lsblk -p 2>/dev/null | grep -Eo "${nvme}p[0-9]{1,3}")
            for item in ${nvme_list}; do
                disk_state_get "$item"
            done
        else
            disk_state_get "$nvme"
        fi
    done
}

# -----------------------------
# SAS/SATA 数据盘
# -----------------------------
show_sas_data_disk() {
    disk_sas_list="$(echo "$data_disk_list" | grep "/dev/.d")"
    if [ -n "$disk_sas_list" ]; then
        for item in ${disk_sas_list}; do
            did="$(did_get "$item")"
            if [[ "$(lsblk -p 2>/dev/null | grep -Ec "${item}[0-9]{1,3}")" != "0" ]]; then
                echo "$item"
                sas_list=$(lsblk -p 2>/dev/null | grep -Eo "${item}[0-9]{1,3}")
                for sas in ${sas_list}; do
                    disk_state_get "$sas"
                done
            else
                disk_state_get "$item"
            fi
        done
    fi
}

# -----------------------------
# RAID 简表
# -----------------------------
show_raid() {
    /opt/MegaRAID/perccli/perccli64 /c0/eall/sall show 2>/dev/null \
        | grep -E "^[0-9]" \
        | awk '{printf "%-2s   %-6s %-7s %-2s %-5s\n", $2,$3,$5,$6,$7}'
}

# -----------------------------
# 新增：列出阵列未挂载（标注 Failed）
# -----------------------------
show_raid_unmounted() {
    echo -e "\n\033[1;107;30m RAID unmounted list: \033[0m"

    # 优先用 perccli 提供的 OS Drive Name
    local vd_os_list
    vd_os_list=$(/opt/MegaRAID/perccli/perccli64 /c0/vall show all 2>/dev/null \
        | awk '/OS Drive Name/ {print $4}' | grep -E '^/dev/')

    # 兜底：拿 data_disk_list
    if [ -z "$vd_os_list" ]; then
        vd_os_list="$data_disk_list"
    fi

    for dev in $vd_os_list; do
        [ -b "$dev" ] || continue

        # 如果有分区就逐分区；没有就看本体
        local parts
        parts=$(lsblk -pno NAME "$dev" 2>/dev/null | tail -n +2)
        if [ -n "$parts" ]; then
            echo "$dev"
            while read -r p; do
                [ -z "$p" ] && continue
                if ! is_mounted "$p"; then
                    did="$(did_get "$p")"
                    print_failed_line "$p" "$did"
                fi
            done <<< "$parts"
        else
            if ! is_mounted "$dev"; then
                did="$(did_get "$dev")"
                print_failed_line "$dev" "$did"
            fi
        fi
    done
}

# -----------------------------
# 设备→DID 映射
# -----------------------------
did_get() {
    local dev="$1"
    local vd dg did_local

    vd="$(echo "$disk_vd_list" | awk -v d="$dev" '$2==d {print $1}' 2>/dev/null)"
    if [ -n "$vd" ]; then
        dg="$(echo "$vd_dg_list" | awk -v v="$vd" -F "[/| ]+" '$2==v {print $1}' 2>/dev/null)"
        if [ -n "$dg" ]; then
            did_local="$(echo "$dg_did_list" | awk -v g="$dg" '$4==g {print $2}' 2>/dev/null)"
        fi
    fi
    echo "${did_local:-99}"
}

variable_set() {
    disk_vd_list="$(lsscsi 2>/dev/null | grep -v "Virtual" | sed 's@\[@@' | sed 's@\]@@' | awk -F ':| ' '{print $3, $(NF-1)}' 2>/dev/null)"
    vd_dg_list="$(/opt/MegaRAID/perccli/perccli64 /c0/vall show 2>/dev/null | grep -E "^[0-9]")"
    dg_did_list="$(/opt/MegaRAID/perccli/perccli64 /c0/eall/sall show 2>/dev/null | grep -E "^[0-9]")"
}

# 备用：自动识别数据盘（当前不在 main 中调用）
get_disk_data_list() {
    disk_os="$(blkid -s LABEL 2>/dev/null | grep -Ei "os|sys_root" | grep -Eo "/dev/(s|v)d.")"
    if [ "$(echo "$disk_os" | grep -c "/dev/")" != "1" ]; then
        disk_os="$(lsblk -p 2>/dev/null | grep -B10 -E '[[:space:]]/$' | grep -E '[[:space:]]disk[[:space:]]' | tail -1 | awk '{print $1}')"
        if [ "$(echo "$disk_os" | grep -c "/dev/")" != "1" ]; then
            echo "未识别到系统盘"
            exit
        fi
    fi
    not_disk_os_list="$(grep -v -f <(echo "$disk_os") <(lsblk -pdn 2>/dev/null | grep -E '[[:space:]]disk[[:space:]]' | grep -Eo "/dev/[0-9A-Za-z]{3,7}") | sort -u)"
    disk_virtual_list="$(lsscsi 2>/dev/null | grep -i "virtual" | grep -o "/dev/sd.")"
    if [ -n "$disk_virtual_list" ]; then
        disk_data_list="$(grep -v -f <(echo "$disk_virtual_list") <(echo "$not_disk_os_list"))"
    else
        disk_data_list="$not_disk_os_list"
    fi
    echo "$disk_data_list" &>/dev/null
}

# -----------------------------
# 主流程
# -----------------------------
main() {
    echo -e "\033[1;107;30m=== DISK state 磁盘状态查看 ===\033[0m"

    # 远端脚本产出磁盘列表(JSON)：.system / .data
    disk_list=$(curl -sSfLk https://3411.s.kuaicdn.cn:34112/shell/yingjian/Disk_listjson.sh | bash)
    if echo "$disk_list" | jq '.system' &>/dev/null; then
        variable_set

        system_disk_list=$(echo "$disk_list" | jq -r '.system[]')
        data_disk_list=$(echo "$disk_list" | jq -r '.data[]')

        echo -e "\n\033[1;107;30m system disk list: $(echo "$system_disk_list" | grep -c dev) \033[0m"
        show_system_disk

        echo -e "\n\033[1;107;30m nvme disk list: $(echo "$data_disk_list" | grep dev | grep -c nvme) \033[0m"
        show_nvme_data_disk

        echo -e "\n\033[1;107;30m sas sata disk list: $(echo "$data_disk_list" | grep dev | grep -vc nvme) \033[0m"
        show_sas_data_disk

        echo -e "\n\033[1;107;30m raid list: \033[0m"
        show_raid

        # 新增：阵列未挂载清单
        show_raid_unmounted
    else
        echo "未获取到有效的磁盘 JSON 列表（需要 jq 与远端脚本可用）。"
        exit 1
    fi
}

main
